package org.pro.core.validateCode.processor;

import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.pro.core.validateCode.ValidateCode;
import org.pro.core.validateCode.ValidateCodeException;
import org.pro.core.validateCode.ValidateCodeGenerator;
import org.pro.core.validateCode.ValidateCodeType;
import org.pro.core.validateCode.repository.ValidateCodeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;

/**
 * 验证码处理
 * @author luoqiz
 *
 * @param <T>
 */
public abstract class AbstractValidateCodeProcessor<T extends ValidateCode> implements ValidateCodeProcessor {

	/**
	 * 收集系统中{@link ValidateCodeGenerator}接口的实现
	 */
	@Autowired
	private Map<String, ValidateCodeGenerator> validateCodeGenerators;

	/**
	 * 验证码存放地址
	 */
	@Autowired
	private ValidateCodeRepository validateCodeRepository;
	
	/**
	 * 验证码操作
	 */
	public void createValidateCode(ServletWebRequest servletWebRequest) throws Exception {
		T validateCode = creatValidateCode(servletWebRequest);
		save(servletWebRequest, validateCode);
		send(servletWebRequest, validateCode);
	}

	/**
	 *  发送校验码，由子类实现
	 *  
	 * @param request
	 * @param validateCode
	 * @throws Exception
	 */
	protected abstract void send(ServletWebRequest servletWebRequest, T validateCode) throws Exception;
	
	/**
	 * 保存验证码
	 * @param servletWebRequest
	 * @param validateCode
	 */
	private void save(ServletWebRequest servletWebRequest, T validateCode) {
		ValidateCode code=new ValidateCode(validateCode.getSaveData() ,validateCode.getLocalDateTime());
		
		validateCodeRepository.save(servletWebRequest,code,getValidateCodeType(servletWebRequest));
		
		//sessionStrategy.setAttribute(request, getSessionKey(request), validateCode);
	}
	

	/**
	 * 生成验证码
	 * @param servletWebRequest
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private T creatValidateCode(ServletWebRequest servletWebRequest) {
		String type = getProcessorType(servletWebRequest);
		ValidateCodeGenerator validateCodeGenerator = validateCodeGenerators.get(type + ValidateCodeGenerator.class.getSimpleName());
		return (T) validateCodeGenerator.generate(servletWebRequest);
	};

	/**
	 * 根据请求的url获取校验码的类型
	 * 
	 * @param request
	 * @return
	 */
	private String getProcessorType(ServletWebRequest request) {
		return StringUtils.substringAfterLast(request.getRequest().getRequestURI(), "/");
	}
	
	private ValidateCodeType getValidateCodeType(ServletWebRequest request) {
		String type=StringUtils.substringBefore(getClass().getSimpleName(), ValidateCodeProcessor.class.getSimpleName());
		return ValidateCodeType.valueOf(type.toUpperCase());
	}
	
	/**
	 * 校验验证码
	 */
	@Override
	public void validate(ServletWebRequest request) {

//		ValidateCode codeInSession =  (ValidateCode)sessionStrategy.getAttribute(servletWebRequest,
//				ValidateCodeProcessor.SESSION_KEY_PREFIX + "sms");
//		String codeInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), "smsCode");
//		
		
		ValidateCodeType codeType = getValidateCodeType(request);

		@SuppressWarnings("unchecked")
		T codeInSession = (T) validateCodeRepository.get(request, codeType);

		String codeInRequest;
		try {
			codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(),
					codeType.getParamNameOnValidate());
		} catch (ServletRequestBindingException e) {
			throw new ValidateCodeException("获取验证码的值失败");
		}

		if (StringUtils.isBlank(codeInRequest)) {
			throw new ValidateCodeException(codeType + "验证码的值不能为空");
		}

		if (codeInSession == null) {
			throw new ValidateCodeException(codeType + "验证码不存在");
		}

		if (codeInSession.isExpried()) {
			validateCodeRepository.remove(request, codeType);
			throw new ValidateCodeException(codeType + "验证码已过期");
		}

		if (!StringUtils.equals(codeInSession.getSaveData(), codeInRequest)) {
			throw new ValidateCodeException(codeType + "验证码不匹配");
		}

		validateCodeRepository.remove(request, codeType);
	}
}
